// License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
// "Business Source License" is a trademark of MariaDB Corporation Ab.

// Parameters

// Licensor:             Union.fi, Labs Inc.
// Licensed Work:        All files under https://github.com/unionlabs/union's sui subdirectory                      
//                       The Licensed Work is (c) 2024 Union.fi, Labs Inc.
// Change Date:          Four years from the date the Licensed Work is published.
// Change License:       Apache-2.0
// 

// For information about alternative licensing arrangements for the Licensed Work,
// please contact info@union.build.

// Notice

// Business Source License 1.1

// Terms

// The Licensor hereby grants you the right to copy, modify, create derivative
// works, redistribute, and make non-production use of the Licensed Work. The
// Licensor may make an Additional Use Grant, above, permitting limited production use.

// Effective on the Change Date, or the fourth anniversary of the first publicly
// available distribution of a specific version of the Licensed Work under this
// License, whichever comes first, the Licensor hereby grants you rights under
// the terms of the Change License, and the rights granted in the paragraph
// above terminate.

// If your use of the Licensed Work does not comply with the requirements
// currently in effect as described in this License, you must purchase a
// commercial license from the Licensor, its affiliated entities, or authorized
// resellers, or you must refrain from using the Licensed Work.

// All copies of the original and modified Licensed Work, and derivative works
// of the Licensed Work, are subject to this License. This License applies
// separately for each version of the Licensed Work and the Change Date may vary
// for each version of the Licensed Work released by Licensor.

// You must conspicuously display this License on each original or modified copy
// of the Licensed Work. If you receive the Licensed Work in original or
// modified form from a third party, the terms and conditions set forth in this
// License apply to your use of that work.

// Any use of the Licensed Work in violation of this License will automatically
// terminate your rights under this License for the current and all other
// versions of the Licensed Work.

// This License does not grant you any right in any trademark or logo of
// Licensor or its affiliates (provided that you may use a trademark or logo of
// Licensor as expressly required by this License).

// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
// AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
// TITLE.

module ibc::ibc_client {
    use std::string::String;

    use sui::clock::Clock;
    use sui::hash::keccak256;

    use ibc::commitment;
    use ibc::events;
    use ibc::light_client::LightClientManager;
    use ibc::state;

    const EBase: u64 = 10100;
    const EClientNotActive: u64 = EBase + 1;

    public(package) fun create_client(
        ibc_uid: &mut UID,
        client_mgr: &mut LightClientManager,
        client_type: String, 
        client_state_bytes: vector<u8>, 
        consensus_state_bytes: vector<u8>,
        ctx: &mut TxContext,        
    ) {
        let client_id = client_mgr.client_count() + 1;

        let (client_state_bytes,
            consensus_state_bytes,
            counterparty_chain_id,
            mut lens_client_event) =
            client_mgr.create_client(
                client_type,
                client_id,
                client_state_bytes,
                consensus_state_bytes,
                ctx,
            );

        if (lens_client_event.is_some()) {
            let lens_client_event = lens_client_event.extract();
            events::emit_create_lens_client(
                lens_client_event.client_id(),
                lens_client_event.l2_chain_id(),
                lens_client_event.l1_client_id(),
                lens_client_event.l2_client_id()
            );
        };

        state::commit(
            ibc_uid,
            commitment::client_state_commitment_key(client_id),
            client_state_bytes
        );

        let latest_height = client_mgr.latest_height(client_id);
        state::commit(
            ibc_uid,
            commitment::consensus_state_commitment_key(client_id, latest_height),
            consensus_state_bytes
        );

        events::emit_create_client(
            client_id,
            client_type,
            counterparty_chain_id
        )
    }

    public(package) fun update_client(        
        ibc_uid: &mut UID,
        client_mgr: &mut LightClientManager,
        clock: &Clock,
        client_id: u32,
        client_message: vector<u8>,
        relayer: address
    ) {
        assert!(client_mgr.status(client_id, clock) == 1, EClientNotActive);

        // Update the client and consensus states using the client message
        let (client_state, consensus_state, height) =
            client_mgr.update_client(client_id, clock, client_message, relayer);

        // Update the client state commitment
        *state::borrow_commitment_mut(ibc_uid, commitment::client_state_commitment_key(client_id)) = client_state;

        // Update the consensus state commitment
        state::add_or_update_commitment(
            ibc_uid,
            commitment::consensus_state_commitment_key(client_id, height),
            keccak256(&consensus_state)
        );

        events::emit_update_client(
            client_id,
            height
        );
    }

    public(package) fun misbehaviour(
        client_mgr: &mut LightClientManager,
        client_id: u32,
        misbehaviour: vector<u8>,
        relayer: address
    ) {
        client_mgr.misbehaviour(client_id, misbehaviour, relayer);

        events::emit_misbehaviour(
            client_id,
        );
    }
}
